package com.dream.libxrec.player;

import android.media.AudioFormat;
import android.media.Image;
import android.media.MediaFormat;
import android.os.SystemClock;
import android.text.TextUtils;
import android.util.Log;
import android.view.Surface;

import com.dream.libxrec.player.audio.AudioDecoder;
import com.dream.libxrec.player.audio.IAudioDecodeCallback;
import com.dream.libxrec.player.video.IVideoDecodeImageCallback;
import com.dream.libxrec.player.video.IVideoDecodeSurfaceCallback;
import com.dream.libxrec.player.video.VideoDecoder;
import com.dream.libxrec.utils.ImageUtils;
import com.dream.libxrec.view.YUVGLSurfaceView;
import com.dream.libxrec.view.YUVSurfaceViewRender;

public class MP4Player {

    private static final String TAG = "MP4Player";
    private String mVideoPath = "";
    private PCMPlayer mPcmPlayer = null;
    private YUVGLSurfaceView mYUVSurface = null;//手动渲染Surface
    private Surface mRenderSurface = null;//直接交给解码器渲染的Surface
    private float mPlayRate = 1.0f;

    private volatile long mVideoPTS = 0L;//视频时间戳
    private volatile long mAudioPTS = 0L;//音频时间戳
    private volatile boolean isPause = false;//是否暂停播放
    private volatile boolean isKeyFramePlay = false;//关键帧播放模式:仅播放关键帧
    private static final byte SYNC_MODE_AUDIO = 1;//以音频时间戳为准进行同步
    private static final byte SYNC_MODE_VIDEO = 2;//以视频时间戳为准进行同步
    private byte mSyncMode = SYNC_MODE_AUDIO;//时间戳同步模式
    private VideoDecoder mVideoDecoder = null;//视频解码器
    private AudioDecoder mAudioDecoder = null;//音频解码器
    private static final long KEY_FRAME_MODE_SLEEP_TIME =
            500;//关键帧播放模式下的单帧休眠时长，单位为毫秒
    private IPlayerListener mPlayerListener = null;
    private boolean isAudioTrack = false;//是否有音频通道
    private boolean isVideoTrack = false;//是否有视频通道
    private int mKeyFrameCount = 0;

    private final IVideoDecodeImageCallback mImageCallback = new IVideoDecodeImageCallback() {
        @Override
        public void init(MediaFormat format) {
            if (mYUVSurface != null){
                final int width =
                        format.getInteger(MediaFormat.KEY_WIDTH);
                final int height =
                        format.getInteger(MediaFormat.KEY_HEIGHT);
                mYUVSurface.init(YUVSurfaceViewRender.RENDER_TYPE_NV12,width,height,true);
            }
        }

        @Override
        public void onImageDecode(Image image, long pts) {

            onVideoSleep(pts);

            if (mYUVSurface != null){
                byte[] data = ImageUtils.getDataFromImage(image,
                        ImageUtils.COLOR_FormatNV12);
                mYUVSurface.onFrame(data);
            }

            image.close();

            if (!isAudioTrack){
                if (mPlayerListener != null){
                    mPlayerListener.onProgressUs(pts);
                }
            }
        }

        @Override
        public void onFinish() {
            isVideoTrack = false;

            if (!isAudioTrack&&mPlayerListener != null){
                mPlayerListener.onFinish();
            }
        }
    };

    private final IVideoDecodeSurfaceCallback mSurfaceCallback = new IVideoDecodeSurfaceCallback() {
        @Override
        public void init(MediaFormat format) {}

        @Override
        public void onSurfaceDecode(long pts) {
            onVideoSleep(pts);
            if (!isAudioTrack){
                if (mPlayerListener != null){
                    mPlayerListener.onProgressUs(pts);
                }
            }
        }

        @Override
        public void onFinish() {
            isVideoTrack = false;

            if (!isAudioTrack&&mPlayerListener != null){
                mPlayerListener.onFinish();
            }
        }
    };

    private void onVideoSleep(long pts){
        if (isKeyFramePlay){
            //控制关键帧休眠固定时长
            SystemClock.sleep(KEY_FRAME_MODE_SLEEP_TIME);
        }else if (mSyncMode == SYNC_MODE_AUDIO){
            while (pts > mAudioPTS&&!isKeyFramePlay){
                //方便进入关键帧模式或音频时间戳更新时，可以及时结束休眠等候
                SystemClock.sleep(1);

                //避免执行seek时被堵塞在这里，或者绘制旧的画面
                if (mVideoDecoder.isSeeking()){
                    return;
                }
            }
        }else{
            if (mVideoPTS == -1||Math.abs(pts - mVideoPTS) > 1000000){
                //当PTS和上一帧的时差大于1秒时，大概率是进行了seek，更新PTS,避免播放异常
                mVideoPTS = pts - 1;
            }

            long interval =
                    (long) ((pts - mVideoPTS)/1000.0/mPlayRate);

            if (interval > 0){
                SystemClock.sleep(interval);
            }
        }

        mVideoPTS = pts;
    }

    private final IAudioDecodeCallback mAudioCB = new IAudioDecodeCallback() {
        @Override
        public void init(MediaFormat format) {
            final int sampleRate =
                    format.getInteger(MediaFormat.KEY_SAMPLE_RATE);
            final int channelNum =
                    format.getInteger(MediaFormat.KEY_CHANNEL_COUNT);
            mPcmPlayer = new PCMPlayer(4060,sampleRate,channelNum, AudioFormat.ENCODING_PCM_16BIT);
        }

        @Override
        public void onDecode(byte[] data, int size, long pts) {
            mAudioPTS = pts;
            if (isKeyFramePlay){
                //关键帧模式下，音频保持和视频时间戳同步，但不播放，确保退出关键帧模式时可以正常播放，尝试了进入该模式时，暂停音频解码，并在退出该模式时用seekTo方法，但是效果不理想，存在半秒的延迟，故放弃

                while (mAudioPTS > mVideoPTS&&isKeyFramePlay){
                    //采用循环的方式多次循环，主要考虑到退出关键帧模式时，音频数据可能处于sleep
                    // 状态，造成卡住的假象
                    SystemClock.sleep(1);
                }

                //关键帧播放模式下不回调播放音频数据
                return;
            }
            if (mPlayerListener != null&&!isSeeking()){
                mPlayerListener.onProgressUs(pts);
            }

            if (mPcmPlayer != null){
                mPcmPlayer.play(data,size);
            }
        }

        @Override
        public void onFinish() {
            isAudioTrack = false;

            if (!isVideoTrack&&mPlayerListener != null){
                mPlayerListener.onFinish();
            }
        }
    };


    public void setPath(String path){
        this.mVideoPath = path;
    }

    /**
     * 先解码为YUV数据，再通过OpenGL转为RGB渲染到Surface上
     * @param s
     */
    public void setSurface(YUVGLSurfaceView s){
        mYUVSurface = s;
        mRenderSurface = null;
    }

    /**
     * 直接渲染到Surface上
     */
    public void setSurface(Surface surface){
        this.mRenderSurface = surface;
        this.mYUVSurface = null;
    }

    public void stop(){
        if (mVideoDecoder != null){
            mVideoDecoder.close();
        }

        if (mAudioDecoder != null){
            mAudioDecoder.close();
        }

        if (mPcmPlayer!= null){
            mPcmPlayer.stop();
            mPcmPlayer = null;
        }

        if (mRenderSurface != null){
            mRenderSurface = null;
        }

        if (mYUVSurface != null){
            mYUVSurface = null;
        }
    }

    public void play(){

        if (TextUtils.isEmpty(mVideoPath)){
            Log.i(TAG,"未设置播放文件!");
            return;
        }

        try {
            mAudioDecoder = new AudioDecoder();
            isAudioTrack = mAudioDecoder.init(mVideoPath,mAudioCB);
        } catch (Exception e) {
            e.printStackTrace();
            isAudioTrack = false;
        }

        try {
            mVideoDecoder = new VideoDecoder();

            if (mRenderSurface != null){
                isVideoTrack = mVideoDecoder.init(mVideoPath,mRenderSurface,mSurfaceCallback);
            }else if (mYUVSurface != null){
                isVideoTrack = mVideoDecoder.init(mVideoPath,mImageCallback);
            }else{
                isVideoTrack = false;
                Log.e(TAG,"surface should not be null");
            }
        }catch (Exception e){
            e.printStackTrace();
            isVideoTrack = false;
        }

        mSyncMode = isAudioTrack?SYNC_MODE_AUDIO:SYNC_MODE_VIDEO;
        //设置时间戳同步模式

        if (isAudioTrack){
            Log.i("MediaMuxer","存在音频通道,采用音频校时");
            new Thread(new Runnable() {
                @Override
                public void run() {
                    try {
                        mAudioDecoder.start();
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
            }).start();
        }

        if (isVideoTrack){
            new Thread(new Runnable() {
                @Override
                public void run() {
                    try {
                        mVideoDecoder.start();
                    }catch (Exception e) {
                        e.printStackTrace();
                    }
                }
            }).start();
        }

        if (mPlayerListener != null){
            mPlayerListener.onReady(getDurationUs(),isAudioTrack,isVideoTrack);
        }
    }

    public boolean isPause(){
        return isPause;
    }

    public void onPause(boolean isPause){
        this.isPause = isPause;

        if (mAudioDecoder != null){
            mAudioDecoder.onPause(isPause);
        }
        if (mVideoDecoder != null){
            mVideoDecoder.onPause(isPause);
        }
    }

    public float getPlayRate(){
        return mPlayRate;
    }

    /**
     * 设置播放速度，1为正常播放，2为2倍数,0.5为慢放2倍
     * @param rate
     */
    public void setPlayRate(float rate){
        this.mPlayRate = rate;

        if (mPcmPlayer != null){
            mPcmPlayer.setTempoChange(rate);
        }
    }

    public boolean isKeyFrameMode(){
        return isKeyFramePlay;
    }

    /**
     * 设置仅播放关键帧的模式（该模式下必须有视频画面，且播放期间不播放音频）
     * @param isKeyFrameMode 当为true时进入模式，false退出模式
     */
    public void setKeyFrameMode(boolean isKeyFrameMode){
        if (!isVideoTrack){
            //没有视频通道，无需设置播放模式
            return;
        }
        this.isKeyFramePlay = isKeyFrameMode;
        this.mVideoDecoder.setDecodeMode(isKeyFrameMode);
    }

    public void setMute(boolean b) {
        if (mPcmPlayer != null){
            mPcmPlayer.setMute(b);
        }
    }

    public boolean isMute() {
        if (mPcmPlayer != null){
            return mPcmPlayer.isMute();
        }

        return false;
    }

    public boolean isSeeking(){
        return mAudioDecoder.isSeeking()||mVideoDecoder.isSeeking();
    }

    /**
     * 获取视频时长，单位为微秒
     * @return
     */
    public long getDurationUs(){
        if (mVideoDecoder != null&&mAudioDecoder != null){
            return Math.max(mVideoDecoder.getDurationUs(),
                    mAudioDecoder.getDurationUs());
        }else if (mVideoDecoder != null){
            return mVideoDecoder.getDurationUs();
        }else if (mAudioDecoder != null){
            return mAudioDecoder.getDurationUs();
        }

        return -1;
    }

    public void setListener(IPlayerListener listener){
        this.mPlayerListener = listener;
    }

    public void seekTo(long pts) {
        new Thread(new Runnable() {
            @Override
            public void run() {
                Log.i(TAG,"seek to "+pts);
                if (mAudioDecoder != null){
                    mAudioDecoder.seekTo(pts);
                }

                if (mVideoDecoder != null){
                    mVideoDecoder.seekTo(pts);
                }

                mAudioPTS = -1;
            }
        }).start();
    }

    public interface IPlayerListener{
        public void onReady(long durationUs,boolean isAudio,boolean isVideo);
        public void onProgressUs(long progressUs);
        public void onFinish();
    }
}
